/*
Copyright (c) 2023 China Mobile Information Technology Co., Ltd
OpenGauss Operator is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/

package controller

import (
	"context"
	"fmt"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/tools/record"
	"k8s.io/klog/v2"
	gsv1 "openGauss-operator/api/v1"
	customClient "openGauss-operator/internal/client"
	"openGauss-operator/internal/service"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/controller"
	"sigs.k8s.io/controller-runtime/pkg/handler"
	"sigs.k8s.io/controller-runtime/pkg/log"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
	"sigs.k8s.io/controller-runtime/pkg/source"
	"time"
)

// OpenGaussClusterReconciler reconciles a OpenGaussCluster object
type OpenGaussClusterReconciler struct {
	K8sClient         customClient.K8sClient
	Scheme            *runtime.Scheme
	Record            record.EventRecorder
	clusterManagement service.ClusterManagementInterface
}

//+kubebuilder:rbac:groups=gaussdb.gaussdb.middleware.cmos.cn,resources=opengaussclusters,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=gaussdb.gaussdb.middleware.cmos.cn,resources=opengaussclusters/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=gaussdb.gaussdb.middleware.cmos.cn,resources=opengaussclusters/finalizers,verbs=update

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the OpenGaussCluster object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.4/pkg/reconcile
func (r *OpenGaussClusterReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {

	logger := log.FromContext(ctx)
	startTime := time.Now()

	// Get OpenGaussCluster Object
	gs, err := r.K8sClient.GetOpenGaussCluster(request.NamespacedName)
	if err != nil {
		if errors.IsNotFound(err) {
			logger.Info("OpenGaussCluster resource not found. Ignoring since object must be deleted")
			return reconcile.Result{}, nil
		}
		logger.Error(err, "Failed to get CR")
		return reconcile.Result{}, err
	}
	logger.Info("Started syncing OpenGaussCluster ")
	defer func() {
		logger.Info("Finished syncing OpenGaussCluster " + fmt.Sprintf("%s", time.Since(startTime)))
	}()

	if err := r.clusterManagement.DeleteDataAfterReleaseCluster(gs); err != nil {
		klog.Error(err)
	}

	if err := r.reconcileOpenGauss(gs); err != nil {
		klog.Error(err)
		return reconcile.Result{RequeueAfter: 10}, nil
	}
	return reconcile.Result{}, nil
}

func (r *OpenGaussClusterReconciler) reconcileOpenGauss(gs *gsv1.OpenGaussCluster) error {

	// Skip manual maintenance
	if maintenance := gs.Spec.Maintenance; maintenance {
		klog.Info(fmt.Sprintf("Manual maintenance, Name : %s, Namespace: %s", gs.Name, gs.Namespace))
		return nil
	}

	//管理集群操作和状态维护
	if err := r.clusterManagement.MaintainClusterStatus(gs); err != nil {
		return err
	}

	return nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *OpenGaussClusterReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error {
	r.clusterManagement = service.NewClusterManagement(r.K8sClient, r.Record)
	return ctrl.NewControllerManagedBy(mgr).
		WithOptions(options).
		For(&gsv1.OpenGaussCluster{}).
		Watches(&source.Kind{Type: &v1.Pod{}}, handler.Funcs{
			CreateFunc:  nil,
			UpdateFunc:  PodUpdateHandler,
			DeleteFunc:  PodDeleteHandler,
			GenericFunc: nil,
		}).
		WithEventFilter(&ResourceLabelPredicate{}).
		Complete(r)
}
